package com.ruoyi.charge.model;

import cn.hutool.core.util.StrUtil;
import com.ruoyi.common.enums.RatePeriodType;
import com.ruoyi.common.utils.BcdCodeTransfer;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.io.Serializable;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;

/**
 * 计费模型（0x0A）消息体对象
 * Billing Model Payload Object (Frame Type 0x0A)
 */
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class BillingModelPayload implements Serializable {
    private static final long serialVersionUID = 1L;


    /**
     * 桩编号 (Pile Number)
     * 7 bytes, BCD
     */
    private String sn;

    /**
     * 计费模型编号 (Billing Model ID)
     * 2 bytes, BCD
     */
    private String modelId;

    /**
     * 尖电费费率 (Peak Power Rate)
     * 4 bytes, BIN code, accurate to 5 decimal places.
     */
    private BigDecimal peakElecFeeRate;

    /**
     * 尖服务费费率 (Peak Service Fee Rate)
     * 4 bytes, BIN code, accurate to 5 decimal places.
     */
    private BigDecimal peakServiceFeeRate;

    /**
     * 峰电费费率 (High Power Rate)
     * 4 bytes, BIN code, accurate to 5 decimal places.
     */
    private BigDecimal highElecFeeRate;

    /**
     * 峰服务费费率 (High Service Fee Rate)
     * 4 bytes, BIN code, accurate to 5 decimal places.
     */
    private BigDecimal highServiceFeeRate;

    /**
     * 平电费费率 (Normal Power Rate)
     * 4 bytes, BIN code, accurate to 5 decimal places.
     */
    private BigDecimal normalElecFeeRate;

    /**
     * 平服务费费率 (Normal Service Fee Rate)
     * 4 bytes, BIN code, accurate to 5 decimal places.
     */
    private BigDecimal normalServiceFeeRate;

    /**
     * 谷电费费率 (Valley Power Rate)
     * 4 bytes, BIN code, accurate to 5 decimal places.
     */
    private BigDecimal valleyElecFeeRate;

    /**
     * 谷服务费费率 (Valley Service Fee Rate)
     * 4 bytes, BIN code, accurate to 5 decimal places.
     */
    private BigDecimal valleyServiceFeeRate;

    /**
     * 计损比例 (Loss Ratio)
     * 1 byte, BIN code.
     */
    private int lossRatio;

    /**
     * 48个时段费率号 (48 Time Slot Rate IDs)
     * Each 1 byte, BIN code.
     * The array index corresponds to the time slot, e.g., index 0 is 00:00-00:30.
     */
    private RatePeriodType[] timeSlotRates;


    /**
     * 从字节数组解析计费模型消息体
     * Parses the billing model payload from a byte array.
     *
     * @param payload The raw byte array for the payload.
     * @return A populated BillingModelPayload object.
     */
    /**
     * 从字节数组解析计费模型消息体。
     * Parses the billing model payload from a byte array.
     *
     * @param payload The raw byte array for the payload.
     * @return A populated BillingModelPayload object.
     */
    public static BillingModelPayload fromBytes(byte[] payload) {
        // Total length according to the provided protocol:
        // 7 (pileNo) + 2 (modelId) + 8*4 (fees) + 1 (lossRatio) + 48*1 (timeSlotRates) = 90 bytes


        // Wrap the payload byte array into a ByteBuffer.
        // Based on the 'convertNumber2ByteArray' function's 'Collections.reverse(hexList)' step,
        // the bytes are arranged in LITTLE_ENDIAN order.
        // Therefore, we must set the ByteBuffer's order to LITTLE_ENDIAN for correct integer interpretation.
        ByteBuffer buffer = ByteBuffer.wrap(payload).order(ByteOrder.LITTLE_ENDIAN); // <--- Changed to LITTLE_ENDIAN

        // 1. 桩编号 (Pile Number) - 7 bytes, BCD
        byte[] pileNoBytes = new byte[7];
        buffer.get(pileNoBytes);
        String pileNo = BcdCodeTransfer.bcdToDecimalString(pileNoBytes);

        // 2. 计费模型编号 (Billing Model ID) - 2 bytes, BCD
        byte[] modelIdBytes = new byte[2];
        buffer.get(modelIdBytes);
        // Convert BCD bytes to decimal string.
        // Fix: Pad the modelId string with leading zeros to ensure it's always 4 digits.
        String modelIdRaw = BcdCodeTransfer.bcdToDecimalString(modelIdBytes);
        String modelId = StrUtil.padPre(modelIdRaw, 4, '0');

        // Helper to read rate values. The divisor is based on the assumption that
        // the integer value represents the BigDecimal multiplied by 100,000 (10^5),
        // as implied by "精确到五位小数".
        BigDecimal divisor = new BigDecimal("100000");

        // 3-10. 8个固定费率 (Fees) - Each 4 bytes (BIN code)
        BigDecimal peakElecFeeRate = new BigDecimal(buffer.getInt()).divide(divisor, 5, RoundingMode.HALF_UP);
        BigDecimal peakServiceFeeRate = new BigDecimal(buffer.getInt()).divide(divisor, 5, RoundingMode.HALF_UP);
        BigDecimal highElecFeeRate = new BigDecimal(buffer.getInt()).divide(divisor, 5, RoundingMode.HALF_UP);
        BigDecimal highServiceFeeRate = new BigDecimal(buffer.getInt()).divide(divisor, 5, RoundingMode.HALF_UP);
        BigDecimal normalElecFeeRate = new BigDecimal(buffer.getInt()).divide(divisor, 5, RoundingMode.HALF_UP);
        BigDecimal normalServiceFeeRate = new BigDecimal(buffer.getInt()).divide(divisor, 5, RoundingMode.HALF_UP);
        BigDecimal valleyElecFeeRate = new BigDecimal(buffer.getInt()).divide(divisor, 5, RoundingMode.HALF_UP);
        BigDecimal valleyServiceFeeRate = new BigDecimal(buffer.getInt()).divide(divisor, 5, RoundingMode.HALF_UP);

        // 11. 计损比例 (Loss Ratio) - 1 byte, BIN code
        int lossRatio = buffer.get() & 0xFF; // Read as unsigned byte

        // 12-59. 48个时段费率号 (48 Time Slot Rate IDs) - Each 1 byte, BIN code
        RatePeriodType[] timeSlotRates = new RatePeriodType[48];
        for (int i = 0; i < 48; i++) {
            byte rateCode = buffer.get();
            timeSlotRates[i] = RatePeriodType.fromCode(rateCode);
        }

        return BillingModelPayload.builder()
                .sn(pileNo)
                .modelId(modelId) // Use the padded modelId
                .peakElecFeeRate(peakElecFeeRate)
                .peakServiceFeeRate(peakServiceFeeRate)
                .highElecFeeRate(highElecFeeRate)
                .highServiceFeeRate(highServiceFeeRate)
                .normalElecFeeRate(normalElecFeeRate)
                .normalServiceFeeRate(normalServiceFeeRate)
                .valleyElecFeeRate(valleyElecFeeRate)
                .valleyServiceFeeRate(valleyServiceFeeRate)
                .lossRatio(lossRatio)
                .timeSlotRates(timeSlotRates)
                .build();
    }


}
